﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;

namespace NHINDirect
{
    public enum TrustEnforcementStatus
    {
        Failed = -1,
        Unknown = 0,
        Success_Offline = 1,            // Signature valid, signing cert is trusted, but could not retrieve cert directly from source
        Success_ThumbprintMismatch = 2, // Signature valid, signing cert is trusted, but the signing cert and the source cert did not match
        Success= 3,                     // Signature valid, siging cert trusted, and certs match perfectly
    }
    
    public interface ITrustModel
    {
        void Enforce(IncomingMessage message);
        void Enforce(OutgoingMessage message);
    }
    
    public class TrustModel : ITrustModel
    {
        public static readonly TrustModel Default = new TrustModel();
        
        TrustChainValidator m_certChainValidator;
                                
        public TrustModel()
            : this(new TrustChainValidator())
        {
        }
        
        public TrustModel(TrustChainValidator validator)
        {
            if (validator == null)
            {
                throw new ArgumentNullException();
            }
            
            m_certChainValidator = validator;
        }
        
        public TrustChainValidator CertChainValidator
        {
            get
            {
                return m_certChainValidator;
            }
        }
                
        public void CheckSignature(IncomingMessage message)
        {
            SignerInfoCollection signers = message.Signature.SignerInfos;
            SignerInfo signer = signers.FindByName(message.Sender.Address);
            if (signer == null)
            {
                signer = signers.FindByName(message.Sender.Host);
            }
            if (signer == null)
            {
                throw new TrustException(TrustError.MissingSenderSignature);
            }
            //
            // Verify the signature
            //
            signer.CheckSignature(true);

            message.ValidatedSigner = signer;
        }
                
        public void Enforce(IncomingMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException();
            }
            
            if (!message.IsSignatureVerified)
            {
                this.CheckSignature(message);
            }
            
            NHINDAddress sender = message.Sender;
            SignerInfo signer = message.ValidatedSigner;
            X509Certificate2 senderCert = sender.HasCertificate ? sender.Certificate : signer.Certificate;
            bool thumbprintVerified = (sender.HasCertificate && sender.Certificate.Thumbprint == signer.Certificate.Thumbprint);

            NHINDAddressCollection recipients = message.DomainRecipients;
            for (int i = 0, count = recipients.Count; i < count; ++i)
            {
                NHINDAddress recipient = recipients[i];
                TrustEnforcementStatus trustStatus = TrustEnforcementStatus.Failed;
                //
                // Does this recipient trust the message sender? 
                //
                if (m_certChainValidator.IsTrustedCertificate(senderCert, recipient.TrustAnchors))
                {
                    if (sender.HasCertificate)
                    {
                        trustStatus = (thumbprintVerified) ? TrustEnforcementStatus.Success : TrustEnforcementStatus.Success_ThumbprintMismatch;
                    }
                    else
                    {
                        trustStatus = TrustEnforcementStatus.Success_Offline;
                    }
                }                    
                recipient.Status = trustStatus;
            }            
        }
        
        public void Enforce(OutgoingMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException();
            }
            
            NHINDAddress sender = message.Sender;
            NHINDAddressCollection recipients = message.Recipients;
            
            for (int i = 0, count = recipients.Count; i < count; ++i)
            {
                NHINDAddress recipient = recipients[i];
                recipient.Status = TrustEnforcementStatus.Failed;                
                if (recipient.HasCertificate && m_certChainValidator.IsTrustedCertificate(recipient.Certificate, sender.TrustAnchors))
                {
                    recipient.Status = TrustEnforcementStatus.Success;
                }
            }
        }
    }        
}
